#ifndef PCQC_H
#define PCQC_H

#include "registration.h"
#include "componentSelection.h"
#include "componentMatch.h"
#include "component.h"
#include "segmentDifferences.h"
#include "check.h"
// QT
#include <qiterator.h>
#include <QMap>
#include <QString>
#include <QColor>
// PCL
#include <pcl/filters/extract_indices.h>
#include <pcl/segmentation/sac_segmentation.h>
#include <pcl/segmentation/extract_clusters.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/filters/statistical_outlier_removal.h>
#include <pcl/segmentation/sac_segmentation.h>
#include <pcl/common/geometry.h>
#include <pcl/sample_consensus/sac_model_perpendicular_plane.h>

/*!
 \file pcqc.h This class contains all the logic of the program.
 \author Sqizzato Stefano
 \author Maroso Alessandro

*/

using namespace pcl;
using namespace std;
// NOTE:

/*!
 \brief This class contains all the logic of the program. The class can easily be converted to use std libraries instead of QT, for QMap (std::map) as for QString (std::string) and color (triple of integers).
 \author Sqizzato Stefano
 \author Maroso Alessandro

*/
class Pcqc
{
public:
    //INIT
/*!
 \brief Default constructor.

*/
    Pcqc();
    /*!
     \brief Load the reference cloud.

     \param[in] path Path to the cloud to load.
     \return bool True if file was loaded correctly.
    */
    bool loadTargetCloud(QString path);
    /*!
     \brief Load the sample cloud.

     \param[in] path Path to the cloud to load.
     \return bool True if file was loaded correctly.
    */
    bool loadSourceCloud(QString path);

    //GETTERS
    /*!
     \brief Gets the target cloud.

     \return PointCloud<PointXYZRGB> the target cloud.
    */
    PointCloud<PointXYZRGB>::Ptr getTargetCloud();
    /*!
     \brief Gets the source cloud.

     \return PointCloud<PointXYZRGB> The target cloud.
    */
    PointCloud<PointXYZRGB>::Ptr getSourceCloud();
    /*!
     \brief Gets the registered cloud.

     \return PointCloud<PointXYZRGB> The registered cloud.
    */
    PointCloud<PointXYZRGB>::Ptr getRegisteredCloud();
    /*!
     \brief Gets the differences cloud.

     \return PointCloud<PointXYZRGB> The differences cloud.
    */
    PointCloud<PointXYZRGB>::Ptr getDifferenceCloud();
    /*!
     \brief Gets the color of the point specified by it's index.

     \param[in] pointIndex The index of the point.
     \return QColor The color of the point.
    */
    QColor* getPointColor(int pointIndex);
    /*!
     \brief Gets the new cloud that will contain the last defined component.

     \return PointCloud<PointXYZRGB> The new cloud that will contain the last defined component.
    */
    PointCloud<PointXYZRGB>::Ptr getNewComponentCloud();
    /*!
     \brief Gets a target component cloud.

     \param[in] componentName The component's name.
     \return PointCloud<PointXYZRGB> The target component cloud.
    */
    PointCloud<PointXYZRGB>::Ptr getTargetComponentCloud(QString componentName);
    /*!
     \brief Gets a source component cloud.

     \param[in] componentName The component's name.
     \return PointCloud<PointXYZRGB> The source component cloud.
    */
    PointCloud<PointXYZRGB>::Ptr getSourceComponentCloud(QString componentName);
    /*!
     \brief Gets the target components list.

     \return QMap<QString, Component> The target components list.
    */
    QMap<QString, Component>* getTargetComponentsList();
    /*!
     \brief Gets the source components list.

     \return QMap<QString, Component> The source components list.
    */
    QMap<QString, Component>* getSourceComponentsList();
    /*!
     \brief Gets the checks list.

     \return QMap<QString, Check> The checks list.
    */
    QMap<QString, Check>* getChecksList();
    /*!
     \brief Checks if the registration has already been performed on the actual couple of clouds.

     \return bool true if the registration has already been performed on the actual couple of clouds.
    */
    bool isRegistrationAlreadyPerformed();

    //SETTERS
    /*!
     \brief Set the euclidean threshold for the clustering.

     \param[in] threshold The new threshold.
    */
    void setClusterSegThreshold(int threshold);
    /*!
     \brief Set the color threshold (0-255) of tolerance for the color segmentation.

     \param[in] threshold The new threshold.
    */
    void setColorSegThreshold(int threshold);
    /*!
     \brief Set the skin thickness for the shape matching.

     \param[in] thickness The new threshold.
    */
    void setShapeMatchingThickness(int thickness);
    /*!
     \brief Set the score and distance threshold for the automatic differences analysis..

     \param[in] scoreThresh The score threshold.
     \param[in] distanceThresh The distance threshold.
    */
    void setScoreDistanceThreshold(float scoreThresh, float distanceThresh);

    //FUNCTIONS
    /*!
     \brief Colors in r,g,b the specified indices of the input cloud.

     \param[in] cloud The cloud to be colored.
     \param[in] indices The indices of the points to be colored.
     \param[in] r The r value.
     \param[in] g The g value.
     \param[in] b The b value.
    */
    void colorIndices(PointCloud<PointXYZRGB>::Ptr cloud, PointIndices::Ptr indices,int r,int g, int b);
    /*!
     \brief Color with random colors the components.

     \param[in] cloud The cloud to be colored.
     \para[in]m components The components list.
    */
    void colorComponents(PointCloud<PointXYZRGB>::Ptr cloud, QMap<QString, Component> *components);
    /*!
     \brief Select a component with the saved thresholds, and color it in green in the newComponentCloud.

     \param selectedPointIndex The index of the point that has been clicked to select the component.
    */
    void componentSelection(int selectedPointIndex);
    /*!
     \brief Segment the new component from the target cloud with the newComponentPointIndices and save it in the newComponentCloud.

     \return bool True if the segmentation succeeded.
    */
    bool componentSegmentation();
    /*!
     \brief Add segmented component to target components list.

     \param[in] componentName The name of the component that will be saved.
     \param[in] isClusterMatching Whether the user chose cluster matching (and not shape matching)
     \return bool True if the component has been succesfully saved.
    */
    bool componentSave(QString componentName, bool isClusterMatching);
    /*!
     \brief Delete component from target components list.

     \param[in] componentName The name of the component that will be deleted.
     \return bool True if the component has been succesfully deleted.
    */
    bool componentDelete(QString componentName);
    /*!
     \brief Add check to the checks list.

     \param[in] checkName The name of the check that will be saved.
     \param[in] componentName The name of the component on which the check will be performed.
     \param[in] type The type of the check.
     \param[in] threshold The threshold value of the check.
     \return bool True if the check has been successfully added.
    */
    bool addCheck(QString checkName, QString componentName, CheckType type, float threshold);
    /*!
     \brief Delete check from the check list.

     \param[in] checkName The name of the check that will be deleted.
     \return bool True if the check has been successfully deleted.
    */
    bool deleteCheck(QString checkName);
    /*!
     \brief Performs registration from the source cloud to the target cloud.

    */
    void registration();
    /*!
     \brief Performs a voxelization of the cloud with the specified leaf size.

     \param[in] input The cloud to be voxelled.
     \param[in] leafSize The leaf size.
     \return PointCloud<PointXYZRGB> The voxelled cloud.
    */
    PointCloud<PointXYZRGB>::Ptr voxelCloud (PointCloud<PointXYZRGB>::Ptr input, double leafSize);
    /*!
     \brief Performs segmentation of the main plane from the cloud.

     \param[in] source The cloud from which remove the plane.
    */
    void segmentation (PointCloud<PointXYZRGB> ::Ptr source);
    /*!
     \brief Removes outliers from the cloud.

     \param[in] cloud The cloud from which remove the outliers.
    */
    void removeOutliers(PointCloud<PointXYZRGB>::Ptr cloud);
    /*!
     \brief Finds components in the source cloud corresponding to the componends of the target cloud specified in componentsList, and saves them internally.

     \return int The number of components found.
    */
    int findSourceComponents();
    /*!
     \brief Automatically finds components in the target cloud and saves them internally.

     \return int The number of components found.
    */
    int findTargetComponents();
    /*!
     \brief Performs a single check basing on the type specified. It's called by performChecks().

     \param[in] source The source component.
     \param[in] target The reference component.
     \param[in] transformation The rigid transformation form the source to the reference component.
     \param[in] type The type of the check to be performed.
     \param[in] threshold The threshold for the check.
     \return bool The result of the check.
    */
    bool singleCheck(Component source, Component target, Eigen::Matrix4f transformation, CheckType type, float threshold);
    /*!
     \brief Performs all the checks stored in the checks list.

     \return QMap<QString, bool> A Dictionary that contains the couples checkName-resultOfTheCheck.
    */
    QMap<QString, bool> performChecks();
    /*!
     \brief Finds the generic differences between the source and the target clouds, without considering defined components.

     \return int The number of differences found in terms of clusters.
    */
    int segmentDifferences();


private:
    // LOCAL VARIABLES
    PointCloud<PointXYZRGB>::Ptr sourceCloud; /*!< the new cloud to be registered and to be checked. */
    PointCloud<PointXYZRGB>::Ptr targetCloud;  /*!< the reference cloud that is correct in every detail. */
    PointCloud<PointXYZRGB>::Ptr registeredCloud;  /*!< the source cloud registered to the target cloud. */
    PointCloud<PointXYZRGB>::Ptr differenceCloud;  /*!< the output cloud from differences segmentation. */
    int colThreshold; /*!< the color threshold (0-255) of tolerance for the manual color segmentation. */
    double cluThreshold; /*!< the euclidean threshold for the manual clustering. */
    double skinThickness; /*!< the skin thickness for shape matching. */
    PointCloud<PointXYZRGB>::Ptr newComponentCloud;  /*!< temp cloud for the component definition */
    PointIndices::Ptr newComponentPointIndices;  /*!< temp point indices for the component definition */
    int lastClickedPointIndex;  /*!< the index of the last point clicked (used to define the component selection). */
    QMap<QString, Component> targetComponentsList;  /*!< dictonary that maps a name of a component with a point indices of the target cloud that define that component. */
    QMap<QString, Component> sourceComponentsList; /*!< list of the components found in the source cloud corresponding to the componentsList ones. */
    QMap<QString, Check> checksList;  /*!< list of the checks added to the ckeck list */
    bool registrationAlreadyPerformed;  /*!< true if registration has already been performed and not needed again. */
    float scoreThreshold,distanceThreshold;  /*!< threshold to use with generic checks */
};

#endif // PCQC_H
